package persistence;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.RollbackException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author Justin Templemore
 * @author hbriand (modified)
 *
 * @class PersistenceUtil
 * @desc A utility class to manage the DB connections
 */

public class PersistenceUtil {

    private static Log log = LogFactory.getLog(PersistenceUtil.class);
    private static EntityManagerFactory emf;
    private static final ThreadLocal<EntityManager> em = new ThreadLocal<EntityManager>();
    private static final ThreadLocal<EntityTransaction> tx = new ThreadLocal<EntityTransaction>();

    public static void init() {
        if (PersistenceUtil.emf == null) {
            PersistenceUtil.emf = Persistence.createEntityManagerFactory("ContactsPU");
        }
    }

    public static void destroy() {
        if (PersistenceUtil.emf != null) {
            PersistenceUtil.emf.close();
        }
    }

    /**
     * Returns an EntityManager for the current thread, creating one if there is none.
     * @return
     */
    public static EntityManager getEM() {
        EntityManager anEM = em.get();
        if (anEM == null || !anEM.isOpen()) {
            log.debug("Opening session");
            anEM = PersistenceUtil.emf.createEntityManager();
            PersistenceUtil.em.set(anEM);
        }
        return anEM;
    }

    /**
     * Close the EM associated to the current thread (if open).
     */
    public static void closeEM() {
        EntityManager anEM = em.get();
        PersistenceUtil.em.set(null);
        if (anEM != null) {
            log.debug("Closing session");
            anEM.close();
        }
    }

    /**
     * Begin a new transaction for the current thread / EM
     * @return
     */
    public static void beginTransaction() {
        EntityTransaction aTx = tx.get();
        // Begin a new transaction, if this Thread has none yet
        if (aTx == null || !aTx.isActive()) {
            log.debug("Beginning a transaction");
            aTx = PersistenceUtil.getEM().getTransaction();
            aTx.begin();
            PersistenceUtil.tx.set(aTx);
        }
    }

    /**
     * Commit the current thread transaction (if active)
     */
    public static void commitTransaction() {
        EntityTransaction aTx = tx.get();
        try {
            if (aTx != null && aTx.isActive()) {
                log.debug("Committing transaction");
                aTx.commit();
            }
            PersistenceUtil.tx.set(null);
        } catch (RollbackException e) {
            PersistenceUtil.rollbackTransaction();
            PersistenceUtil.closeEM();
        }
    }

    /**
     * Rollback the current thread transaction (if active and marked for rollback)
     */
    public static void rollbackTransaction() {
        EntityTransaction aTx = tx.get();
        if (aTx != null && aTx.isActive() && aTx.getRollbackOnly()) {
            log.debug("Rollback transaction");
            aTx.rollback();
        }
        PersistenceUtil.tx.set(null);
        PersistenceUtil.closeEM();
    }
}
